#ifndef __M_LOG_H__
#define __M_LOG_H__
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <mutex>
#include <atomic>
#include <stdarg.h>
#include <unordered_map>
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"

class Logger
{
public:
    using ptr = std::shared_ptr<Logger>;
    Logger(const std::string& logger_name, LogLevel::value level,
           Formatter::ptr& formatter, std::vector<LogSink::ptr>& sinks)
        :_logger_name(logger_name), _limit_level(level), 
         _formatter(formatter), _sinks(sinks) 
         // sinks.begin  sinks.end
    {}
    // 完成构造日志消息格式化，得到字符串
    void debug(const std::string& file, size_t line, const std::string& fmt, ...)
    {
        // 通过传入参数构造日志消息对象，格式化使出，最终落地
        // 1.判断当前日志是否到了输出等级
        if (LogLevel::value::DEBUG < _limit_level)
        {
            return;
        }
        // 2.对fmt格式化字符串和不定参组织，得到日志消息字符串
        va_list va;
        va_start(va, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), va);
        if (ret == -1)
        {
            std::cout << "vasprintf failed!\n";
        }
        va_end(va);
        serialize(LogLevel::value::DEBUG, file, line, res);
        // 6.释放res
        free(res);
    }
    void info(const std::string& file, size_t line, const std::string& fmt, ...)
    {
        // 通过传入参数构造日志消息对象，格式化使出，最终落地
        // 1.判断当前日志是否到了输出等级
        if (LogLevel::value::INFO < _limit_level)
        {
            return;
        }
        // 2.对fmt格式化字符串和不定参组织，得到日志消息字符串
        va_list va;
        va_start(va, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), va);
        if (ret == -1)
        {
            std::cout << "vasprintf failed!\n";
        }
        va_end(va);
        serialize(LogLevel::value::INFO, file, line, res);
        // 6.释放res
        free(res);
    }
    void warn(const std::string& file, size_t line, const std::string& fmt, ...)
    {
        // 通过传入参数构造日志消息对象，格式化使出，最终落地
        // 1.判断当前日志是否到了输出等级
        if (LogLevel::value::WARN < _limit_level)
        {
            return;
        }
        // 2.对fmt格式化字符串和不定参组织，得到日志消息字符串
        va_list va;
        va_start(va, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), va);
        if (ret == -1)
        {
            std::cout << "vasprintf failed!\n";
        }
        va_end(va);
        serialize(LogLevel::value::WARN, file, line, res);
        // 6.释放res
        free(res);
    }
    void error(const std::string& file, size_t line, const std::string& fmt, ...)
    {
        // 通过传入参数构造日志消息对象，格式化使出，最终落地
        // 1.判断当前日志是否到了输出等级
        if (LogLevel::value::ERROR < _limit_level)
        {
            return;
        }
        // 2.对fmt格式化字符串和不定参组织，得到日志消息字符串
        va_list va;
        va_start(va, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), va);
        if (ret == -1)
        {
            std::cout << "vasprintf failed!\n";
        }
        va_end(va);
        serialize(LogLevel::value::ERROR, file, line, res);
        // 6.释放res
        free(res);
    }
    void fatal(const std::string& file, size_t line, const std::string& fmt, ...)
    {
        // 通过传入参数构造日志消息对象，格式化使出，最终落地
        // 1.判断当前日志是否到了输出等级
        if (LogLevel::value::FATAL < _limit_level)
        {
            return;
        }
        // 2.对fmt格式化字符串和不定参组织，得到日志消息字符串
        va_list va;
        va_start(va, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), va);
        if (ret == -1)
        {
            std::cout << "vasprintf failed!\n";
        }
        va_end(va);
        serialize(LogLevel::value::FATAL, file, line, res);
        // 6.释放res
        free(res);
    }

    const std::string& name()
    {
        return _logger_name;
    }

protected:
    // 对上面冗余部分封装
    void serialize(LogLevel::value level, const std::string& file, size_t line, char* str)
    {
        // 3.构造LogMsg对象
        LogMsg msg(level, line, file, _logger_name, str);
        // 4.格式化工具对LogMsg格式化，得到格式化后的日志字符串
        std::stringstream ss;
        _formatter->format(ss, msg);
        // 5.落地
        log(ss.str().c_str(), ss.str().size());
    }
    // 输出日志，不同日志器由不同的落地方式
    virtual void log(const char* data, size_t len) = 0;
protected:
    std::mutex _mutex;  // 互斥锁，输出线程安全，不会出现交叉日志
    std::string _logger_name;  // 日志器名，唯一标识
    // 频繁访问， 加锁
    std::atomic<LogLevel::value> _limit_level;  // 限制等级，超过才可以输出
    Formatter::ptr _formatter;  // 格式化模块对象
    std::vector<LogSink::ptr> _sinks;  // 落地模块对象数组
};

// 同步日志器
class SyncLogger :public Logger
{
public:
    SyncLogger(const std::string &logger_name, LogLevel::value level,
               Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
        : Logger(logger_name, level, formatter, sinks)
    {
    }
protected:
    // 同步，日志通过落地模块句柄直接落地
    void log(const char *data, size_t len)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_sinks.empty())
            return;
        for (auto& sink : _sinks)
        {
            sink->log(data, len);
        }
    }
};

// 异步日志器
class AsyncLogger :public Logger
{
public:
    AsyncLogger(const std::string& logger_name, LogLevel::value level,
           Formatter::ptr& formatter, std::vector<LogSink::ptr>& sinks,
           AsyncType looper_type)
        :Logger(logger_name, level, formatter, sinks),
        _looper(std::make_shared<AsyncLooper>(
                std::bind(&AsyncLogger::realLog, this, std::placeholders::_1),
                looper_type))
    {}

    // 数据写入缓冲区
    void log(const char* data, size_t len)
    {
        _looper->push(data, len);
    }
    // 实际落地函数
    void realLog(Buffer& buf)
    {
        if (_sinks.empty())
            return;
        for (auto& sink : _sinks)
        {
            sink->log(buf.begin(), buf.readableSize());
        }
    }

private:
    AsyncLooper::ptr _looper;
};

/*
    使用建造者类建造日志器，不需要用户构造，简化使用
    1.抽象日志器建造者类（完成零件的构建&日志的构建）
        （1）设置日志器类型
        （2）将不同类型的日志器创建放到同一个日志器建造者类完成
*/
enum class LoggerType
{
    LOGGER_SYNC = 0,
    LOGGER_ASYNC
};
class LoggerBuilder
{
public:
    LoggerBuilder() 
                :_logger_type(LoggerType::LOGGER_ASYNC), 
                _limit_level(LogLevel::value::DEBUG),
                _looper_type(AsyncType::ASYNC_SAFE)
    {}
    void buildLoggerType(LoggerType type) { _logger_type = type; }
    void buildLoggerName(const std::string &name) { _logger_name = name; }
    void buildEnabelUnSafAsynce() { _looper_type = AsyncType::ASYNC_UNSAFE; }
    void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }
    void buildFormatter(const std::string &pattern)
    {
        _formatter = std::make_shared<Formatter>(pattern);
    }
    template <typename SinkType, typename... Args>
    void buildSink(Args&& ...args)
    {
        LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
        _sinks.push_back(psink);
    }
    virtual Logger::ptr build() = 0;
protected:
    LoggerType _logger_type;
    std::string _logger_name;
    LogLevel::value _limit_level;
    Formatter::ptr _formatter;
    std::vector<LogSink::ptr> _sinks;
    AsyncType _looper_type;
};

// 2.派生具体的建造者类--局部日志器建造者 & 全局日志器建造者（添加了全局单例管理器后，加入全局管理）
class LocalLoggerBuilder :public LoggerBuilder
{
public:
    Logger::ptr build() override
    {
        assert(!_logger_name.empty());  // 必须有日志器名
        if (_formatter.get() == nullptr)
        {
            _formatter = std::make_shared<Formatter>();
        }

        if (_sinks.empty())
        {
            buildSink<StdoutSink>();
        }

        if (_logger_type == LoggerType::LOGGER_ASYNC)
        {
            // 异步
            return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
        }

        // 同步
        return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
    }
};

class LoggerManager
{
public:
    static LoggerManager &getInstance()
    {
        static LoggerManager eton;
        return eton;
    }

    void addLogger(Logger::ptr& logger)
    {
        if (hasLogger(logger->name()))
            return;
        std::unique_lock<std::mutex> lock(_mutex);
        _loggers.insert(std::make_pair(logger->name(), logger));
    }

    bool hasLogger(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _loggers.find(name);
        if (it == _loggers.end())
        {
            return false;
        }

        return true;
    }

    Logger::ptr getLogger(const std::string& name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _loggers.find(name);
        if (it == _loggers.end())
        {
            return Logger::ptr();
        }

        return it->second;      
    }
    
    Logger::ptr rootLogger()
    {
        return _root_logger;
    }

private:
    LoggerManager()
    {
        std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
        builder->buildLoggerName("root");
        _root_logger = builder->build();
        _loggers.insert(std::make_pair("root", _root_logger));
    }

private:
    std::mutex _mutex;
    Logger::ptr _root_logger;  // 默认日志器
    std::unordered_map<std::string, Logger::ptr> _loggers;
};

// 全局日志器建造者，加入单例对象中
class GlobalLoggerBuilder :public LoggerBuilder
{
public:
    Logger::ptr build() override
    {
        assert(!_logger_name.empty());  // 必须有日志器名
        if (_formatter.get() == nullptr)
        {
            _formatter = std::make_shared<Formatter>();
        }

        if (_sinks.empty())
        {
            buildSink<StdoutSink>();
        }

        Logger::ptr logger;
        if (_logger_type == LoggerType::LOGGER_ASYNC)
        {
            // 异步
            logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
        }
        else
        {
            // 同步
        logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }

        LoggerManager::getInstance().addLogger(logger);
        return logger;
    }
};
#endif